package com.tengju.bff.interfaces.manage.star.facade;

import com.google.common.collect.Lists;
import com.tengju.bff.interfaces.app.user.assembler.StarSignInfoAssembler;
import com.tengju.bff.interfaces.app.user.assembler.UserInfoAssembler;
import com.tengju.bff.interfaces.manage.club.vo.ClubVO;
import com.tengju.bff.interfaces.manage.star.vo.*;
import com.tengju.bff.interfaces.shared.MobileVO;
import com.tengju.bff.interfaces.shared.PageAssembler;
import com.tengju.data.application.star.StarIndicatorService;
import com.tengju.data.domain.model.report.AchievementBonus;
import com.tengju.data.domain.model.report.AchievementBonusLogRepository;
import com.tengju.data.domain.model.report.MarketReport;
import com.tengju.data.domain.model.user.StarIndicator;
import com.tengju.data.domain.shared.DateUtil;
import com.tengju.data.domain.shared.tree.Forest;
import com.tengju.data.domain.shared.tree.StableTree;
import com.tengju.data.domain.shared.tree.Tree;
import com.tengju.data.domain.shared.tree.TreeNode;
import com.tengju.user.application.archives.dto.BlackListQueryDTO;
import com.tengju.user.application.archives.dto.StarAuditQueryDTO;
import com.tengju.user.application.archives.dto.StarChangeRecordDetailVO;
import com.tengju.user.application.user.UserDocumentService;
import com.tengju.user.domain.model.doc.UserDocument;
import com.tengju.user.domain.model.sign.*;
import com.tengju.user.domain.model.user.*;
import com.tengju.user.domain.service.RemoteUserService;
import com.tengju.user.domain.service.UserDocService;
import com.tengju.user.domain.shared.BasePage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class StarSignManageFacade {

    private final StarSignInfoRepository starSignInfoRepository;
    private final StarIndicatorRepository starIndicatorRepository;
    private final StarIndicatorService starIndicatorService;
    private final StarLimitRepository starLimitRepository;
    private final UserDocService userDocService;
    private final StarChangeRecordRepository starChangeRecordRepository;
    private final RemoteUserService remoteUserService;
    private final AchievementBonusLogRepository achievementBonusLogRepository;
    private final UserDocumentService userDocumentService;

    public ListStarAuditRecordVO getListStarAuditList(StarAuditQueryDTO starAuditQueryDTO) {
        PageAssembler.convert(starAuditQueryDTO);
        StarSignStatus starSignStatus = StarSignStatus.fromVal(starAuditQueryDTO.getSignStatus());
        BasePage<StarSignInfo> starSignInfoBasePage = starSignInfoRepository.listPageStarSignInfo(
                Optional.ofNullable(starAuditQueryDTO.getIdCode())
                        .map(UserIdCode::new)
                        .orElse(null),
                starAuditQueryDTO.getMobile(),
                starAuditQueryDTO.getNickName(),
                starAuditQueryDTO.getAuditDesc(),
                Optional.ofNullable(starSignStatus).map(StarSignStatus::getAuditStat).orElse(null),
                Optional.ofNullable(starSignStatus).map(StarSignStatus::getStep).orElse(Lists.newArrayList()),
                starAuditQueryDTO.getHandleType(),
                starAuditQueryDTO.getOriginStarLevel(),
                starAuditQueryDTO.getTargetStarLevel(),
                starAuditQueryDTO.getBlackListFlag(),
                starAuditQueryDTO.getOffset(),
                starAuditQueryDTO.getPageSize()
        );
        ListStarAuditRecordVO listStarAuditRecordVO = new ListStarAuditRecordVO();
        List<StarSignInfo> starSignInfoList = starSignInfoBasePage.getDatas();
        if (CollectionUtils.isEmpty(starSignInfoList)) {
            return listStarAuditRecordVO;
        }
        List<UserIdCode> idCodes = starSignInfoList.stream()
                .map(StarSignInfo::getIdCode)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        List<UserInfo> userInfoList = userDocService.searchUserByIdCodes(idCodes);
        Map<Long, UserInfo> userInfoMap = userInfoList.stream()
                .collect(Collectors.toMap(k -> k.getIdCode().getValue(), v -> v, (v1, v2) -> v1));

        List<StarLimitInfo> starLimitInfos = starLimitRepository.listStarLimitByIdCodes(idCodes);
        Map<Long, StarLimitInfo> starLimitInfoMap = starLimitInfos.stream()
                .collect(Collectors.toMap(k -> k.getIdCode().getValue(), v -> v, (v1, v2) -> v1));
        List<StarAuditRecordVO> starAuditRecordVOS = starSignInfoList.stream().map(v -> {
            StarAuditRecordVO starAuditRecordVO = StarSignInfoAssembler.entityToAuditRecordVO(v);
            UserInfo userInfo = userInfoMap.get(starAuditRecordVO.getIdCode());
            if (userInfo == null) {
                return starAuditRecordVO;
            }
            starAuditRecordVO.setMobile(new MobileVO(userInfo.getWholePhoneNumber()));
            starAuditRecordVO.setNickName(userInfo.getNickName());
            StarLimitInfo starLimitInfo = starLimitInfoMap.get(userInfo.getIdCode().getValue());
            if (starLimitInfo != null && starLimitInfo.getLimitHighestStarLevel() <= v.getStarLevel()) {
                starAuditRecordVO.setBlackListFlag(true);
            }
            return starAuditRecordVO;
        }).collect(Collectors.toList());
        listStarAuditRecordVO.setStarAuditRecordVOList(starAuditRecordVOS);
        listStarAuditRecordVO.setPageIndex(starAuditQueryDTO.getPageIndex());
        listStarAuditRecordVO.setPageSize(starAuditQueryDTO.getPageSize());
        listStarAuditRecordVO.setTotal(starSignInfoBasePage.getTotal());
        return listStarAuditRecordVO;
    }

    public StarAuditDetailVO getStarAuditDetail(Long auditId) {
        List<StarSignInfo> starSignInfoList = starSignInfoRepository.batchGetByIds(Collections.singletonList(auditId));
        if (CollectionUtils.isEmpty(starSignInfoList)) {
            return null;
        }
        StarSignInfo starSignInfo = starSignInfoList.get(0);
        long idCode = starSignInfo.getIdCode().getValue();
        List<UserStarInfo> userStarInfos = starSignInfoRepository.listUserStarInfoByIdCodes(Collections.singletonList(idCode));
        UserStarInfo userStarInfo = userStarInfos.get(0);

        return initStarAuditDetailVO(userStarInfo);
    }

    public StarAuditDetailVO initStarAuditDetailVO(UserStarInfo userStarInfo) {
        StarAuditDetailVO starAuditDetailVO = new StarAuditDetailVO();
        StarSignInfo starSignInfo = userStarInfo.getStarSignInfo();
        if (starSignInfo != null) {
            starAuditDetailVO.setId(starSignInfo.getId().getValue());
            //指标标准
            List<StarConfIndicator> starConfIndicatorList = starIndicatorRepository.listConfIndicator(starSignInfo.getStarLevel());
            if (!CollectionUtils.isEmpty(starConfIndicatorList)) {
                List<StarConfIndicatorVO> starConfIndicatorVOList = starConfIndicatorList.stream()
                        .map(StarSignInfoAssembler::starConfIndicatorToVO)
                        .collect(Collectors.toList());
                starAuditDetailVO.setStarConfIndicatorVOList(starConfIndicatorVOList);
            }
        }

        UserIdCode idCode = userStarInfo.getIdCode();

        //星级变更记录
        List<StarChangeRecord> starChangeRecordList = starChangeRecordRepository.listStarChangeRecord(idCode);
        if (!CollectionUtils.isEmpty(starChangeRecordList)) {
            List<StarChangeRecordDetailVO> changeRecordDetailVOS = starChangeRecordList.stream()
                    .map(UserInfoAssembler::starChangeRecordToVO)
                    .collect(Collectors.toList());
            starAuditDetailVO.setStarChangeRecordDetailList(changeRecordDetailVOS);
        }

        //数据审核记录
        List<StarSignInfo> historySignRecords = starSignInfoRepository.getHistoryByIdCode(idCode);
        if (!CollectionUtils.isEmpty(historySignRecords)) {
            List<StarAuditRecordVO> historyStarAuditRecordVOs = historySignRecords.stream()
                    .map(StarSignInfoAssembler::entityToAuditRecordVO)
                    .collect(Collectors.toList());
            starAuditDetailVO.setStarAuditListInfoList(historyStarAuditRecordVOs);
        }

        //服务商详情
        UserStarLevelDetailVO userStarLevelDetailVO = StarSignInfoAssembler.userStarToStarLevelDetailVO(userStarInfo);
        starAuditDetailVO.setUserStarLevelDetail(userStarLevelDetailVO);

        //黑名单信息
        List<StarLimitInfo> starLimitInfos = starLimitRepository.listStarLimit(idCode);
        if (!CollectionUtils.isEmpty(starLimitInfos)) {
            List<BlackNameListDetailVO> blackNameListDetailVOList = starLimitInfos.stream()
                    .map(StarSignInfoAssembler::starLimitToBlackDetailVO)
                    .collect(Collectors.toList());
            starAuditDetailVO.setBlackNameListDetailList(blackNameListDetailVOList);
        }

        //指标数据
        List<StarIndicator> starIndicatorStatList = starIndicatorService
                .listStarIndicatorStat(new com.tengju.data.domain.model.user.UserIdCode(idCode.getValue()));
        if (!CollectionUtils.isEmpty(starIndicatorStatList)) {
            List<StarIndicatorDetailVO> starIndicatorDetailVOS = starIndicatorStatList.stream()
                    .map(StarSignInfoAssembler::starIndicatorStatToVO)
                    .collect(Collectors.toList());
            starAuditDetailVO.setStarIndicatorDetailList(starIndicatorDetailVOS);
        }

        // 俱乐部信息
        UserDocument userDocument = userDocumentService.findById(idCode);
        if (userDocument != null && userDocument.getClubId() > 0) {
            ClubVO clubVO = new ClubVO();
            clubVO.setClubId(userDocument.getClubId());
            clubVO.setClubName(userDocument.getClubName());
            starAuditDetailVO.setClub(clubVO);
        }
        return starAuditDetailVO;
    }

    public StarAuditDetailVO getBlackDetail(Long idCode) {
        List<UserStarInfo> userStarInfos = starSignInfoRepository.listUserStarInfoByIdCodes(Collections.singletonList(idCode));
        UserStarInfo userStarInfo = userStarInfos.get(0);
        return initStarAuditDetailVO(userStarInfo);
    }


    public ListBlackUserDetailVO getListBlackList(BlackListQueryDTO blackListQueryDTO) {
        PageAssembler.convert(blackListQueryDTO);
        BasePage<Long> idCodePage = starSignInfoRepository.listPageBlackUser(
                Optional.ofNullable(blackListQueryDTO.getIdCode())
                        .filter(l -> l > 0)
                        .map(UserIdCode::new)
                        .orElse(null),
                blackListQueryDTO.getMobile(),
                blackListQueryDTO.getNickName(),
                blackListQueryDTO.getStarLevel(),
                blackListQueryDTO.getAgreementStatus(),
                blackListQueryDTO.getAgreementFromDate(),
                blackListQueryDTO.getAgreementEndDate(),
                blackListQueryDTO.getBlackListFlag(),
                blackListQueryDTO.getOffset(),
                blackListQueryDTO.getPageSize()
        );
        List<Long> idCodes = idCodePage.getDatas();

        List<UserStarInfo> userStarInfos = starSignInfoRepository.listUserStarInfoByIdCodes(idCodes);
        List<BlackUserDetailVO> blackUserDetailVOList = userStarInfos.stream()
                .map(StarSignInfoAssembler::entityToBlackUserDetailVO)
                .collect(Collectors.toList());
        ListBlackUserDetailVO listBlackUserDetailVO = new ListBlackUserDetailVO();
        listBlackUserDetailVO.setBlackUserDetailVOList(blackUserDetailVOList);
        listBlackUserDetailVO.setPageIndex(blackListQueryDTO.getPageIndex());
        listBlackUserDetailVO.setPageSize(blackListQueryDTO.getPageSize());
        listBlackUserDetailVO.setTotal(idCodePage.getTotal());
        return listBlackUserDetailVO;
    }

    public List<SignStatisticsDetail> listSignStatisticsDetail() {
        List<SignStatisticsDetail> signStatisticsDetailList = starSignInfoRepository.listSignStatisticsDetail();
        if (CollectionUtils.isEmpty(signStatisticsDetailList)) {
            return Collections.emptyList();
        }
        List<UserIdCode> userIdCodeList = signStatisticsDetailList.stream()
                .filter(v -> !StarSignStepEnum.END.equals(v.getSignStep()) && !StarSignStepEnum.SIGN_ALREADY.equals(v.getSignStep()))
                .map(SignStatisticsDetail::getIdCode).collect(Collectors.toList());
        List<UserInfo> userInfoList = remoteUserService.getByIdList(userIdCodeList);
        Map<Long, UserInfo> userInfoMap = userInfoList.stream()
                .collect(Collectors.toMap(k -> k.getIdCode().getValue(), v -> v, (v1, v2) -> v1));
        for (SignStatisticsDetail signStatisticsDetail : signStatisticsDetailList) {
            long idCode = signStatisticsDetail.getIdCode().getValue();
            UserInfo userInfo = userInfoMap.get(idCode);
            if (userInfo == null) {
                continue;
            }
            signStatisticsDetail.setMobile(userInfo.getWholePhoneNumber());
        }
        return signStatisticsDetailList;
    }


    public List<SignStatisticsDetail> starList(StarLevelEnum limitStarLevel) {
        List<SignStatisticsDetail> signStatisticsDetailList = starSignInfoRepository.listSignStatisticsDetail();
        if (CollectionUtils.isEmpty(signStatisticsDetailList)) {
            return Collections.emptyList();
        }
        List<SignStatisticsDetail> result = signStatisticsDetailList.stream()
                .filter(d -> d.getStarLevel().getCode() >= limitStarLevel.getCode())
                .collect(Collectors.toList());

        List<UserIdCode> userIdCodeList = result.stream()
                .map(SignStatisticsDetail::getIdCode).collect(Collectors.toList());

        List<UserInfo> userInfoList = remoteUserService.getByIdList(userIdCodeList);
        Map<Long, UserInfo> userInfoMap = userInfoList.stream()
                .collect(Collectors.toMap(k -> k.getIdCode().getValue(), v -> v, (v1, v2) -> v1));

        Map<Long, StarLevelEnum> starMap = result.stream()
                .collect(Collectors.toMap(k -> k.getIdCode().getValue(), SignStatisticsDetail::getStarLevel, (v1, v2) -> v1));

        initForest();
        for (SignStatisticsDetail signStatisticsDetail : result) {
            if (signStatisticsDetail.getStarLevel().getCode() < limitStarLevel.getCode()) {
                continue;
            }
            long idCode = signStatisticsDetail.getIdCode().getValue();
            UserInfo userInfo = userInfoMap.get(idCode);
            if (userInfo == null) {
                continue;
            }
            signStatisticsDetail.setMobile(userInfo.getWholePhoneNumber());
            signStatisticsDetail.setName(userInfo.getName());
            signStatisticsDetail.setNickname(userInfo.getNickName());

            UserInfo upper = userInfo.getPidCode() == null ? null : userInfoMap.get(userInfo.getPidCode().getValue());
            if (upper == null && userInfo.getPidCode() != null) {
                upper = remoteUserService.getByIdCode(userInfo.getPidCode());
            }
            if (upper != null) {
                signStatisticsDetail.setUpperCode(upper.getIdCode());
                signStatisticsDetail.setUpperName(upper.getName());
                signStatisticsDetail.setUpperNickname(upper.getNickName());
            }

            if (userInfo.getStarLevel().equals(StarLevelEnum.EC.getCode())) {
                signStatisticsDetail.setEcId(String.valueOf(idCode));
                signStatisticsDetail.setEcName(userInfo.getName());
                signStatisticsDetail.setEcNickname(userInfo.getNickName());
            } else {
                Long ecId = findExactlySuperiorUser(idCode, StarLevelEnum.EC, starMap);
                if (ecId == null && userInfo.getPidCode() != null) {
                    ecId = findExactlySuperiorUser(userInfo.getPidCode().getValue(), StarLevelEnum.EC, starMap);
                }
                UserInfo ec = ecId == null ? null : userInfoMap.get(ecId);
                if (ec != null) {
                    signStatisticsDetail.setEcId(ec.getIdCode().toString());
                    signStatisticsDetail.setEcName(ec.getName());
                    signStatisticsDetail.setEcNickname(ec.getNickName());
                }
            }

            if (userInfo.getStarLevel().equals(StarLevelEnum.SEC.getCode())) {
                signStatisticsDetail.setSecId(String.valueOf(idCode));
                signStatisticsDetail.setSecName(userInfo.getName());
                signStatisticsDetail.setSecNickname(userInfo.getNickName());
            } else {
                Long secId = findSuperiorUser(idCode, StarLevelEnum.SEC, starMap);
                if (secId == null && userInfo.getPidCode() != null) {
                    secId = findSuperiorUser(userInfo.getPidCode().getValue(), StarLevelEnum.SEC, starMap);
                }
                UserInfo sec = secId == null ? null : userInfoMap.get(secId);
                if (sec != null) {
                    signStatisticsDetail.setSecId(sec.getIdCode().toString());
                    signStatisticsDetail.setSecName(sec.getName());
                    signStatisticsDetail.setSecNickname(sec.getNickName());
                }
            }

        }
        return result;
    }


    private Long findExactlySuperiorUser(Long id, StarLevelEnum starLevel, Map<Long, StarLevelEnum> starMap) {
        Long target = null;
        for (StableTree<MarketReport> tree : forest.getStableTrees()) {
            TreeNode<MarketReport> node = tree.findNode(new com.tengju.data.domain.model.user.UserIdCode(id));
            if (node != null) {
                StarLevelEnum star = starMap.get(node.getId().getValue());
                if (star != null && star.getCode().equals(starLevel.getCode())) {
                    target = node.getId().getValue();
                    break;
                } else if (node.getParent() != null) {
                    target = findSuperiorUser(node.getParent().getId().getValue(), starLevel, starMap);
                }
                if (target != null) {
                    break;
                }
            }
        }
        return target;
    }

    private Long findSuperiorUser(Long id, StarLevelEnum starLevel, Map<Long, StarLevelEnum> starMap) {
        Long target = null;
        for (StableTree<MarketReport> tree : forest.getStableTrees()) {
            TreeNode<MarketReport> node = tree.findNode(new com.tengju.data.domain.model.user.UserIdCode(id));
            if (node != null) {
                StarLevelEnum star = starMap.get(node.getId().getValue());
                if (star != null && star.getCode() >= starLevel.getCode()) {
                    target = node.getId().getValue();
                    break;
                } else if (node.getParent() != null) {
                    target = findSuperiorUser(node.getParent().getId().getValue(), starLevel, starMap);
                }
                if (target != null) {
                    break;
                }
            }
        }
        return target;
    }

    private Forest<MarketReport> forest;

    private void initForest() {
        forest = new Forest<>();
        LocalDateTime logTime = LocalDateTime.now().withMinute(30).withSecond(0).withNano(0);
        if (LocalDateTime.now().getMinute() < 30) {
            logTime = logTime.minusHours(1);
        }
        Integer month = DateUtil.formatMonthInt(logTime);
        List<AchievementBonus> achievementBonuses =
                achievementBonusLogRepository.listByLogTimeAndMonth(logTime, month);
        Map<Long, List<AchievementBonus>> idCodeMidInfoMap = achievementBonuses.stream()
                .collect(Collectors.groupingBy(v -> v.getIdCode().getValue()));

        List<Future<Tree<MarketReport>>> taskList = new ArrayList<>();
        for (Long idCode : idCodeMidInfoMap.keySet()) {
            List<AchievementBonus> userAchievementBonusCalMidInfoList = idCodeMidInfoMap.get(idCode);
            AchievementBonus midInfo = userAchievementBonusCalMidInfoList.get(0);
            com.tengju.data.domain.model.user.UserIdCode userPIdCode = midInfo.getPIdCode();
            MarketReport btr = new MarketReport(
                    com.tengju.data.domain.model.user.UserIdCode.of(idCode),
                    null,
                    null,
                    null,
                    null,
                    logTime,
                    month,
                    null
            );
            Future<Tree<MarketReport>> task = forest.asyncPutNode(btr, userPIdCode);
            taskList.add(task);
        }
        taskList.forEach(task -> {
            try {
                task.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });
        forest.finishSetUp();
    }
}
